from tkinter import *
from tkinter import ttk, font, filedialog as fd
import shutil, os, ctypes, pyglet
from fractions import Fraction
from PIL import ImageTk, Image, ImageOps, ImageDraw, ImageFont
import Program
import MyPantry
from Side_frame import Side_frame
from View_recipe_frame import View_recipe_frame
from Recipe import Recipe
from In_cabinet import In_cabinet
from In_refrigerator import In_refrigerator

class Recipes_frame(Frame):
    """
    Purpose:
        frame to easily view recipes that you can make with the
        ingredients you have or search recipes by name
    Instance variables:
        self.cached_path: saved path of the recipe image
        self.cached_color: saved color of the rounded corner colors for the background
        self.button_bg_hex_color: hex value of the background of the arrow buttons
        self.lower_frame: frame for buttons to switch between recipe and pantry frame
        self.my_pantry_tab_button: button to switch to my pantry frame
        self.recipes_tab_button: button to switch to recipes frame
        self.lower_middle_frame: frame to display current displayed recipe
        self.current_recipe_num_entry_box: entry box to select which
            ingredient is being viewed
        self.current_recipe_num_label: label to show how many recipes there are
        self.top_frame: frame for side menu button and search recipe box
        self.side_menu_button: button to open side menu frame to edit recipes and
            ingredients
        self.search_recipes_combo_box: combo box to search recipes with
        self.recipes_main_label: label showing what is displayed. Either recipes
            from search or recipes based on pantry
        self.middle_frame: frame containing information on the recipe being displayed
        self.recipe_list_left_button: button to go down in the currently displayed
            recipe
        self.center_frame: frame containing image, instructions, name, and
            missing ingredients of a recipe
        self.recipe_list_right_button: button to go up in the currently displayed
            recipe
        self.recipes_based_on_ingredients_list: cached list of recipes found
            from searching with ingredients in cabinet and refrigerator
        self.missing_ingredients_for_recipe_dict: dictionary containing ingredients
            that you are missing for the recipes
        self.recipes_based_on_search_list: recipe list found based on the search
        self.current_recipe_based_on_ingredients: recipe number that is currently
            being displayed for recipes based on pantry
        self.current_recipe_based_on_search: recipe number that is currently
            being displayed for recipes based on searched
        self.use_search_recipes: bool on if search recipes are being displayed
        self.image_of_recipe_label: label that displays the image of recipe
        self.recipe_name_label: label to display recipe name
        self.recipe_instructions_label: label to display recipe instructions
        self.missing_ingredients_label: label to display missing ingredients
    Methods:
        search_recipes_combo_box_clicked_event: changes the search mode to display
            recipes based on search
        update_show_recipe_from_entry_box: runs when a number is entered in the
            text box to change the viewable recipe
        limit_num_to_bound: takes a number and if it is heigher than the max num,
            it is set to the max num. If lower than min, is set to lowest num
        search_recipes_by_name: handles searching recipes based on name
        display_recipes_from_search: handles displaying recipes found from search
        set_wordwrap: changes text word wrap length for recipe name, missing
            ingrediets, and instructions
        refresh_window: changes displayed recipe when a new search is entered
        update_frame: updates the frame to display the correct recipe
        increment: changes the recipe that is viewed one up or one down
        display_recipe_from_ingredients: handles displaying recipes based
            on pantry ingredients
        set_no_recipes_found: sets the window in a way that
            displays that no recipes are found
        update_recipes_from_ingredients_list: searches and stores recipes based
            on pantry ingredients
        update_instructions: sets the instructions label to display some
            text of the instructions
        set_image: sets the image label to display the correctly sized image of recipe
        set_arrows_images: changes the left and right toggle buttons to an image
        change_colors: changes the frame color scheme to given colors
    """

    current_recipe = 0

    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.cached_path = None
        self.cached_color = None
        self.button_bg_hex_color = None
        self.lower_frame = Frame(self)
        self.lower_frame.pack(side="bottom", pady=(0, 20))
        self.my_pantry_tab_button = Button(
            self.lower_frame,
            text="My Pantry",
            command=lambda: Program.Program.controller.change_frames(MyPantry.MyPantry),
        )
        self.my_pantry_tab_button.grid(row=0, column=0, padx=(0, 5))
        self.recipes_tab_button = Button(
            self.lower_frame, text="Recipes", state="disabled"
        )
        self.recipes_tab_button.grid(row=0, column=1, padx=(5, 0))
        self.lower_middle_frame = Frame(self)
        self.lower_middle_frame.pack(side="bottom")
        self.lower_middle_frame.grid_rowconfigure(0, weight=1)
        self.lower_middle_frame.grid_columnconfigure(0, weight=1)
        self.lower_middle_frame.grid_columnconfigure(1, weight=1)
        self.lower_middle_frame_rounded_image_label = Label(self.lower_middle_frame)
        self.lower_middle_frame_rounded_image_label.grid(row=0, column=0, columnspan=2)
        self.current_recipe_num_entry_box = Entry(
            self.lower_middle_frame, width=3, justify="right"
        )
        self.current_recipe_num_entry_box.bind(
            "<KeyPress>",
            lambda event: Program.Program.only_allow_int(event),
        )
        self.current_recipe_num_entry_box.bind(
            "<KeyRelease>", lambda e: self.update_show_recipe_from_entry_box(e)
        )
        self.current_recipe_num_entry_box.grid(row=0, column=0, sticky="e")
        self.current_recipe_num_label = Label(self.lower_middle_frame, text="OF 10")
        self.current_recipe_num_label.grid(row=0, column=1, sticky="w")

        self.top_frame = Frame(self)
        self.top_frame.pack(fill="x", pady=(10, 0))
        self.side_menu_button = Button(
            self.top_frame,
            text="|||",
            command=lambda: Program.Program.controller.frames[Side_frame].open_menu(),
        )
        self.side_menu_button.pack(side="left")
        self.search_recipes_combo_box = ttk.Combobox(self.top_frame, width=27, height=5)
        self.search_recipes_combo_box_clicked = False
        self.search_recipes_combo_box.set("Search For Recipe")
        self.search_recipes_combo_box.bind(
            "<KeyRelease>", lambda e: self.update_frame(True)
        )
        self.search_recipes_combo_box.bind(
            "<<ComboboxSelected>>", lambda e: self.update_frame(True)
        )
        self.search_recipes_combo_box.bind(
            "<Button-1>",
            lambda _: (
                (
                    Program.Program.controller.default_combo_value_clear(
                        self.search_recipes_combo_box, "Search For Recipe"
                    ),
                    self.search_recipes_combo_box_clicked_event(),
                )
                if not self.search_recipes_combo_box_clicked
                else None,
            ),
        )
        self.search_recipes_combo_box.pack(expand=True, fill="both")

        self.recipes_main_label = Label(
            self,
            font=Program.Program.resizing_title_font,
            borderwidth=1,
            relief="solid",
        )
        self.recipes_main_label.pack(pady=(10, 10), fill="x")

        self.middle_frame = Frame(self, borderwidth=1, relief="solid")
        self.middle_frame.pack(fill="both", expand=True)
        self.middle_frame.rowconfigure(0, weight=1)
        self.middle_frame.columnconfigure(0, weight=0)
        self.middle_frame.columnconfigure(1, weight=1)
        self.middle_frame.columnconfigure(2, weight=0)
        self.recipe_list_left_button = Button(
            self.middle_frame,
            text="🡄",
            command=lambda: (self.increment(False), self.update_frame()),
            borderwidth=0,
        )
        self.recipe_list_left_button.grid(row=0, column=0, sticky="nsw")
        self.center_frame = Frame(self.middle_frame)
        self.center_frame.grid(row=0, column=1, sticky="nsew")
        self.center_frame.bind(
            "<Button-1>",
            lambda _: (
                Program.Program.controller.frames[View_recipe_frame].set_page_to_recipe(
                    self.recipe_name_label.cget("text"),
                    self.missing_ingredients_for_recipe_dict.get(
                        self.recipe_name_label.cget("text")
                    ),
                ),
                Program.Program.controller.change_frames(View_recipe_frame),
            )
            if self.current_recipe_num_label.cget("text")[-2:] != " 0"
            else None,
        )
        self.recipe_list_right_button = Button(
            self.middle_frame,
            text="🡆",
            command=lambda: (self.increment(True), self.update_frame()),
            borderwidth=0,
        )
        self.recipe_list_right_button.grid(row=0, column=2, sticky="nse")

        self.recipes_based_on_ingredients_list = []
        self.missing_ingredients_for_recipe_dict = {}
        self.recipes_based_on_search_list = []
        self.current_recipe_based_on_ingredients = 0
        self.current_recipe_based_on_search = 0
        self.use_search_recipes = False
        self.image_of_recipe_label = Label(self.center_frame)
        self.image_of_recipe_label.bind(
            "<Button-1>",
            lambda _: (
                Program.Program.controller.frames[View_recipe_frame].set_page_to_recipe(
                    self.recipe_name_label.cget("text"),
                    self.missing_ingredients_for_recipe_dict.get(
                        self.recipe_name_label.cget("text")
                    ),
                ),
                Program.Program.controller.change_frames(View_recipe_frame),
            )
            if self.current_recipe_num_label.cget("text")[-2:] != " 0"
            else None,
        )
        self.image_of_recipe_label.pack(pady=(10, 0))
        self.recipe_name_label = Label(
            self.center_frame, font=Program.Program.resizing_pantry_font
        )
        self.recipe_name_label.bind(
            "<Button-1>",
            lambda _: (
                Program.Program.controller.frames[View_recipe_frame].set_page_to_recipe(
                    self.recipe_name_label.cget("text"),
                    self.missing_ingredients_for_recipe_dict.get(
                        self.recipe_name_label.cget("text")
                    ),
                ),
                Program.Program.controller.change_frames(View_recipe_frame),
            )
            if self.current_recipe_num_label.cget("text")[-2:] != " 0"
            else None,
        )
        self.recipe_name_label.pack()
        self.recipe_instructions_label = Label(
            self.center_frame, font=Program.Program.resizing_pantry_font
        )
        self.recipe_instructions_label.bind(
            "<Button-1>",
            lambda _: (
                Program.Program.controller.frames[View_recipe_frame].set_page_to_recipe(
                    self.recipe_name_label.cget("text"),
                    self.missing_ingredients_for_recipe_dict.get(
                        self.recipe_name_label.cget("text")
                    ),
                ),
                Program.Program.controller.change_frames(View_recipe_frame),
            )
            if self.current_recipe_num_label.cget("text")[-2:] != " 0"
            else None,
        )
        self.recipe_instructions_label.pack()
        self.missing_ingredients_label = Label(
            self.center_frame,
            font=Program.Program.resizing_pantry_font,
        )
        self.missing_ingredients_label.bind(
            "<Button-1>",
            lambda _: (
                Program.Program.controller.frames[View_recipe_frame].set_page_to_recipe(
                    self.recipe_name_label.cget("text"),
                    self.missing_ingredients_for_recipe_dict.get(
                        self.recipe_name_label.cget("text")
                    ),
                ),
                Program.Program.controller.change_frames(View_recipe_frame),
            )
            if self.current_recipe_num_label.cget("text")[-2:] != " 0"
            else None,
        )
        self.missing_ingredients_label.pack()
        self.update_recipes_from_ingredients_list()
        self.search_recipes_by_name()
        self.update_frame()

    def search_recipes_combo_box_clicked_event(self):
        self.search_recipes_combo_box_clicked = True

    def update_show_recipe_from_entry_box(self, event):
        if event.char.isnumeric():
            num = int("0" + self.current_recipe_num_entry_box.get())
            if self.use_search_recipes:
                num = self.limit_num_to_bound(
                    num, 1, len(self.recipes_based_on_search_list)
                )
                self.current_recipe_based_on_search = num - 1
                self.update_frame()
            else:
                num = self.limit_num_to_bound(
                    num, 1, len(self.recipes_based_on_ingredients_list)
                )
                self.current_recipe_based_on_ingredients = num - 1
                self.update_frame()

    def limit_num_to_bound(self, num, low, high):
        if num > high:
            num = high
        elif num < low:
            num = low
        return num

    def search_recipes_by_name(self):
        current_text = self.search_recipes_combo_box.get().upper()
        if (
            not self.search_recipes_combo_box_clicked
            and current_text == "SEARCH FOR RECIPE"
        ):
            current_text = ""
        found_recipes_dict = {}
        for recipe_name in Recipe.recipes_dict:
            indx = recipe_name.upper().find(current_text)
            if indx != -1:
                found_recipes_dict.setdefault(indx, list())
                found_recipes_dict[indx].append(recipe_name)
        self.recipes_based_on_search_list = []
        for key in sorted(tuple(found_recipes_dict.keys())):
            self.recipes_based_on_search_list += sorted(found_recipes_dict[key])
        self.search_recipes_combo_box["values"] = self.recipes_based_on_search_list

    def display_recipes_from_search(self, recipe_num_to_show):
        if len(self.recipes_based_on_search_list) != 0:
            recipe_name = self.recipes_based_on_search_list[recipe_num_to_show]
            self.recipe_name_label.config(text=recipe_name)
            self.update_instructions(recipe_name)
            self.set_image(f"images/{recipe_name}.png")
            missing_ingredients_list = sorted(
                list(
                    set(Program.Program.search_recipe_name(recipe_name).ingredients.keys())
                    - set(In_cabinet.cabinet_list + In_refrigerator.refrigerator_list)
                )
            )
            missing_ingredients = ", ".join(missing_ingredients_list)
            if missing_ingredients == "":
                self.missing_ingredients_label.config(text="No Missing Ingredients")
                self.missing_ingredients_label.config(
                    fg=Program.Program.convert_rgb_to_hex(
                        Program.Program.overlay_colors(
                            Program.Program.convert_hex_to_rgb("#90EE90"),
                            Program.Program.convert_hex_to_rgb("#000000"),
                            0.87,
                        )
                    ),
                    bg="#90EE90",
                )
            else:
                self.missing_ingredients_label.config(
                    text="Missing Ingredients: " + missing_ingredients
                )
                if len(missing_ingredients_list) <= 5:
                    self.missing_ingredients_label.config(
                        fg=Program.Program.convert_rgb_to_hex(
                            Program.Program.overlay_colors(
                                Program.Program.convert_hex_to_rgb("#FFFF00"),
                                Program.Program.convert_hex_to_rgb("#000000"),
                                0.87,
                            )
                        ),
                        bg="#FFFF00",
                    )
                else:
                    self.missing_ingredients_label.config(
                        fg=Program.Program.convert_rgb_to_hex(
                            Program.Program.overlay_colors(
                                Program.Program.convert_hex_to_rgb("#CD5C5C"),
                                Program.Program.convert_hex_to_rgb("#000000"),
                                0.87,
                            )
                        ),
                        bg="#CD5C5C",
                    )

        else:
            self.set_no_recipes_found()

    def set_wordwrap(self):
        self.recipe_name_label.config(wraplength=self.center_frame.winfo_width())
        self.recipe_instructions_label.config(
            wraplength=self.center_frame.winfo_width()
        )
        self.missing_ingredients_label.config(
            wraplength=self.center_frame.winfo_width()
        )
        self.set_image(f"images/{self.recipe_name_label['text']}.png")

    def refresh_window(self):
        self.update_recipes_from_ingredients_list()
        self.current_recipe_based_on_search = 0
        self.update_frame()

    def update_frame(self, from_search=False):
        if from_search:
            self.current_recipe_based_on_search = 0
            if self.search_recipes_combo_box.get().capitalize() == "":
                self.use_search_recipes = False
            else:
                self.use_search_recipes = True
        elif (
            self.use_search_recipes
            and self.search_recipes_combo_box.get().capitalize() == ""
        ):
            self.use_search_recipes = False

        if self.use_search_recipes:
            self.search_recipes_by_name()
            self.recipes_main_label["text"] = "Recipes based on search"
            self.display_recipes_from_search(self.current_recipe_based_on_search)
            self.current_recipe_num_entry_box.delete(0, END)
            self.current_recipe_num_entry_box.insert(
                0, str(self.current_recipe_based_on_search + 1)
            )
            self.current_recipe_num_label.config(
                text=f"of {len(self.recipes_based_on_search_list)}"
            )
            self.current_recipe_num_entry_box.config(
                width=len(str(len(self.recipes_based_on_search_list))) + 1
            )

        else:
            self.search_recipes_by_name()
            self.display_recipe_from_ingredients(
                self.current_recipe_based_on_ingredients
            )
            self.recipes_main_label["text"] = "Recipes based on your pantry"
            self.current_recipe_num_entry_box.delete(0, END)
            self.current_recipe_num_entry_box.insert(
                0, str(self.current_recipe_based_on_ingredients + 1)
            )
            self.current_recipe_num_label.config(
                text=f"of {len(self.recipes_based_on_ingredients_list)}"
            )
            self.current_recipe_num_entry_box.config(
                width=len(str(len(self.recipes_based_on_ingredients_list))) + 1
            )

    def increment(self, up):
        if self.use_search_recipes:
            if up:
                self.current_recipe_based_on_search += 1
                if self.current_recipe_based_on_search == len(
                    self.recipes_based_on_search_list
                ):
                    self.current_recipe_based_on_search = 0
            else:
                self.current_recipe_based_on_search -= 1
                if self.current_recipe_based_on_search < 0:
                    self.current_recipe_based_on_search = (
                        len(self.recipes_based_on_search_list) - 1
                    )
            self.current_recipe_num_entry_box.delete(0, END)
            self.current_recipe_num_entry_box.insert(
                0, str(self.current_recipe_based_on_search + 1)
            )
        else:
            if up:
                self.current_recipe_based_on_ingredients += 1
                if self.current_recipe_based_on_ingredients == len(
                    self.recipes_based_on_ingredients_list
                ):
                    self.current_recipe_based_on_ingredients = 0
            else:
                self.current_recipe_based_on_ingredients -= 1
                if self.current_recipe_based_on_ingredients < 0:
                    self.current_recipe_based_on_ingredients = (
                        len(self.recipes_based_on_ingredients_list) - 1
                    )
            self.current_recipe_num_entry_box.delete(0, END)
            self.current_recipe_num_entry_box.insert(
                0, str(self.current_recipe_based_on_ingredients + 1)
            )

    def display_recipe_from_ingredients(self, recipe_num_to_show):
        if len(self.recipes_based_on_ingredients_list) != 0:
            recipe_name = self.recipes_based_on_ingredients_list[recipe_num_to_show]
            self.recipe_name_label.config(text=recipe_name)
            self.update_instructions(recipe_name)
            self.set_image(f"images/{recipe_name}.png")
            missing_ingredients_list = sorted(
                list(
                    self.missing_ingredients_for_recipe_dict[
                        self.recipes_based_on_ingredients_list[recipe_num_to_show]
                    ]
                )
            )
            missing_ingredients = ", ".join(missing_ingredients_list)
            if missing_ingredients == "":
                self.missing_ingredients_label.config(text="No Missing Ingredients")
                self.missing_ingredients_label.config(
                    fg=Program.Program.convert_rgb_to_hex(
                        Program.Program.overlay_colors(
                            Program.Program.convert_hex_to_rgb("#90EE90"),
                            Program.Program.convert_hex_to_rgb("#000000"),
                            0.87,
                        )
                    ),
                    bg="#90EE90",
                )
            else:
                self.missing_ingredients_label.config(
                    text="Missing Ingredients: " + missing_ingredients
                )
                if len(missing_ingredients_list) <= 5:
                    self.missing_ingredients_label.config(
                        fg=Program.Program.convert_rgb_to_hex(
                            Program.Program.overlay_colors(
                                Program.Program.convert_hex_to_rgb("#FFFF00"),
                                Program.Program.convert_hex_to_rgb("#000000"),
                                0.87,
                            )
                        ),
                        bg="#FFFF00",
                    )
                else:
                    self.missing_ingredients_label.config(
                        fg=Program.Program.convert_rgb_to_hex(
                            Program.Program.overlay_colors(
                                Program.Program.convert_hex_to_rgb("#CD5C5C"),
                                Program.Program.convert_hex_to_rgb("#000000"),
                                0.87,
                            )
                        ),
                        bg="#CD5C5C",
                    )
        else:
            self.set_no_recipes_found()

    def set_no_recipes_found(self):
        self.set_image("")
        self.recipe_name_label.config(text="No Recipes Found")
        self.recipe_instructions_label.config(text="")
        self.missing_ingredients_label.config(bg=self.center_frame["bg"])
        self.missing_ingredients_label.config(text="")

    def update_recipes_from_ingredients_list(self):
        self.current_recipe_based_on_ingredients = 0
        (
            self.recipes_based_on_ingredients_list,
            self.missing_ingredients_for_recipe_dict,
        ) = Program.Program.search_recipes_by_ingredients(
            set(In_refrigerator.refrigerator_list + In_cabinet.cabinet_list)
        )

    def update_instructions(self, recipe_name):
        recipe = Program.Program.search_recipe_name(recipe_name)
        text = recipe.instructions[0:101]
        count_of_newlines = text.count("\n")
        if count_of_newlines > 3:
            last_found = 0
            for i in range(3):
                last_found = text.find("\n", last_found) + 1
            text = text[:last_found] + "..."
        elif len(text) > 100:
            text = text[0:-1] + "..."
        elif text == "":
            text = "No Instructions"
        self.recipe_instructions_label.config(text=text)

    def set_image(self, path):
        try:
            if (
                self.cached_path != path
                or self.cached_color != self.image_of_recipe_label["bg"]
            ):
                if "#" in self.image_of_recipe_label["bg"]:
                    color = self.image_of_recipe_label["bg"].upper()
                else:
                    color = "#FFFFFF"
                color_rgb = Program.Program.convert_hex_to_rgb(color)
                self.image_of_recipe = Image.open(path)
                self.rounded_corner_overlay = Image.new(
                    "RGBA",
                    (
                        self.image_of_recipe.width,
                        self.image_of_recipe.height,
                    ),
                    "#FFFFFF00",
                )
                self.rounded_corner_overlay_draw = ImageDraw.Draw(
                    self.rounded_corner_overlay
                )
                self.rounded_corner_overlay_draw.rounded_rectangle(
                    (
                        0,
                        0,
                        self.image_of_recipe.width,
                        self.image_of_recipe.height,
                    ),
                    outline=color + "FF",
                    width=3,
                    radius=22,
                )
                ImageDraw.floodfill(
                    self.rounded_corner_overlay,
                    xy=(1, 1),
                    value=tuple(list(color_rgb) + [255]),
                    thresh=200,
                )
                ImageDraw.floodfill(
                    self.rounded_corner_overlay,
                    xy=(self.image_of_recipe.width - 1, 1),
                    value=tuple(list(color_rgb) + [255]),
                    thresh=200,
                )
                ImageDraw.floodfill(
                    self.rounded_corner_overlay,
                    xy=(
                        self.image_of_recipe.width - 1,
                        self.image_of_recipe.height - 1,
                    ),
                    value=tuple(list(color_rgb) + [255]),
                    thresh=200,
                )
                ImageDraw.floodfill(
                    self.rounded_corner_overlay,
                    xy=(1, self.image_of_recipe.height - 1),
                    value=tuple(list(color_rgb) + [255]),
                    thresh=200,
                )
                self.image_of_recipe = Image.alpha_composite(
                    self.image_of_recipe, self.rounded_corner_overlay
                )
                self.cached_path = path
                self.cached_color = self.image_of_recipe_label["bg"]

            self.final_width = self.center_frame.winfo_width() // 3
            self.final_height = self.center_frame.winfo_height() // 2
            self.image_of_recipe_resized = ImageOps.contain(
                self.image_of_recipe, (self.final_width, self.final_height)
            )
            self.image_of_recipe_tkinter = ImageTk.PhotoImage(
                self.image_of_recipe_resized
            )
            self.image_of_recipe_label.config(image=self.image_of_recipe_tkinter)
        except:
            self.image_of_recipe_label.config(image="")

    def set_arrows_images(self, bg_hex_color=None):
        if bg_hex_color == None:
            bg_hex_color = self.button_bg_hex_color
        else:
            self.button_bg_hex_color = bg_hex_color
        if bg_hex_color != None:
            left_arrow_img = Image.open("gui/icons/left.png")
            if Program.Program.light_mode:
                ImageDraw.floodfill(
                    left_arrow_img,
                    xy=(15, 20),
                    value=tuple(list((255, 255, 255)) + [255]),
                    thresh=200,
                )
            left_arrow_img_w, left_arrow_img_h = left_arrow_img.size
            self.left_button_image = Image.new(
                "RGBA",
                (
                    self.recipe_list_left_button.winfo_width(),
                    self.recipe_list_left_button.winfo_height(),
                ),
                self.center_frame["bg"] + "FF",
            )
            left_button_draw = ImageDraw.Draw(self.left_button_image)
            left_button_draw.rounded_rectangle(
                (
                    0,
                    0,
                    self.recipe_list_left_button.winfo_width(),
                    self.recipe_list_left_button.winfo_height(),
                ),
                fill=bg_hex_color + "FF",
                radius=22,
            )
            left_button_image_w, left_button_image_h = self.left_button_image.size
            offset = (
                (left_button_image_w - left_arrow_img_w) // 2,
                (left_button_image_h - left_arrow_img_h) // 2,
            )
            self.left_button_image.paste(left_arrow_img, offset, left_arrow_img)
            self.left_arrow_image_tkinter = ImageTk.PhotoImage(self.left_button_image)
            self.recipe_list_left_button.config(
                image=self.left_arrow_image_tkinter,
            )

            right_arrow_img = Image.open("gui/icons/right.png")
            if Program.Program.light_mode:
                ImageDraw.floodfill(
                    right_arrow_img,
                    xy=(23, 20),
                    value=tuple(list((255, 255, 255)) + [255]),
                    thresh=200,
                )
            right_arrow_img_w, right_arrow_img_h = right_arrow_img.size
            self.right_button_image = Image.new(
                "RGBA",
                (
                    self.recipe_list_right_button.winfo_width(),
                    self.recipe_list_right_button.winfo_height(),
                ),
                self.center_frame["bg"] + "FF",
            )
            right_button_draw = ImageDraw.Draw(self.right_button_image)
            right_button_draw.rounded_rectangle(
                (
                    0,
                    0,
                    self.recipe_list_right_button.winfo_width(),
                    self.recipe_list_right_button.winfo_height(),
                ),
                fill=bg_hex_color + "FF",
                radius=22,
            )
            right_button_image_w, right_button_image_h = self.right_button_image.size
            offset = (
                (right_button_image_w - right_arrow_img_w) // 2,
                (right_button_image_h - right_arrow_img_h) // 2,
            )
            self.right_button_image.paste(right_arrow_img, offset, right_arrow_img)
            self.right_arrow_image_tkinter = ImageTk.PhotoImage(self.right_button_image)
            self.recipe_list_right_button.config(
                image=self.right_arrow_image_tkinter,
            )

    def change_colors(self, bg_color, primary_color, primary_variant, secondary_color):
        elevation_1_color = Program.Program.convert_rgb_to_hex(
            Program.Program.overlay_colors(
                Program.Program.convert_hex_to_rgb(bg_color), (255, 255, 255), 0.05
            )
        )
        if Program.Program.light_mode:
            button_text_color = "#FFFFFF"
            label_color = Program.Program.convert_rgb_to_hex(
                Program.Program.overlay_colors(
                    Program.Program.convert_hex_to_rgb(elevation_1_color), (0, 0, 0), 0.87
                )
            )
        else:
            button_text_color = "#000000"
            label_color = Program.Program.convert_rgb_to_hex(
                Program.Program.overlay_colors(
                    Program.Program.convert_hex_to_rgb(elevation_1_color), (255, 255, 255), 0.87
                )
            )

        self.config(bg=bg_color)

        self.search_recipes_combo_box.configure(
            style="colored.TCombobox",
        )
        self.search_recipes_combo_box.tk.eval(
            f"[ttk::combobox::PopdownWindow %s].f.l configure -foreground {label_color} -background {elevation_1_color}"
            % self.search_recipes_combo_box
        )
        self.side_menu_button.config(
            bg=primary_color,
            fg=Program.Program.convert_rgb_to_hex(
                Program.Program.overlay_colors(
                    Program.Program.convert_hex_to_rgb(secondary_color),
                    Program.Program.convert_hex_to_rgb(button_text_color),
                    0.87,
                )
            ),
            border="0",
            relief="flat",
            borderwidth=0,
            highlightthickness=0,
            activebackground=primary_color,
        )
        self.recipes_main_label.config(bg=elevation_1_color, fg=label_color)
        self.middle_frame.config(bg=elevation_1_color)
        self.center_frame.config(bg=elevation_1_color)
        self.set_arrows_images(secondary_color)
        self.recipe_list_left_button.config(
            border="0",
            relief="flat",
            borderwidth=0,
            highlightthickness=0,
            activebackground=elevation_1_color,
        )
        self.recipe_list_right_button.config(
            border="0",
            relief="flat",
            borderwidth=0,
            highlightthickness=0,
            activebackground=elevation_1_color,
        )
        self.image_of_recipe_label.config(bg=elevation_1_color)
        self.set_image(self.cached_path)
        self.recipe_name_label.config(bg=elevation_1_color, fg=label_color)
        self.recipe_instructions_label.config(bg=elevation_1_color, fg=label_color)

        self.lower_middle_frame.config(bg=secondary_color)
        self.lower_middle_frame_rounded_image = Program.Program.create_rounded_button_image(
            bg_color,
            secondary_color,
            Program.Program.convert_rgb_to_hex(
                Program.Program.overlay_colors(
                    Program.Program.convert_hex_to_rgb(secondary_color),
                    Program.Program.convert_hex_to_rgb(button_text_color),
                    0,
                )
            ),
            "",
            int(
                (
                    self.current_recipe_num_label.winfo_width()
                    + self.current_recipe_num_entry_box.winfo_width()
                )
                * 1.3
            ),
            self.current_recipe_num_label.winfo_height(),
            22,
        )
        self.lower_middle_frame_rounded_image_label.config(
            image=self.lower_middle_frame_rounded_image, bg=bg_color
        )
        self.current_recipe_num_label.config(
            bg=secondary_color,
            fg=Program.Program.convert_rgb_to_hex(
                Program.Program.overlay_colors(
                    Program.Program.convert_hex_to_rgb(secondary_color),
                    Program.Program.convert_hex_to_rgb(button_text_color),
                    0.87,
                )
            ),
        )
        self.current_recipe_num_entry_box.config(
            bg=secondary_color,
            fg=Program.Program.convert_rgb_to_hex(
                Program.Program.overlay_colors(
                    Program.Program.convert_hex_to_rgb(secondary_color),
                    Program.Program.convert_hex_to_rgb(button_text_color),
                    0.87,
                )
            ),
            borderwidth=0,
            highlightthickness=0,
        )

        self.lower_frame.config(bg=bg_color)
        self.my_pantry_tab_button_image = Program.Program.create_rounded_button_image(
            bg_color,
            primary_color,
            Program.Program.convert_rgb_to_hex(
                Program.Program.overlay_colors(
                    Program.Program.convert_hex_to_rgb(primary_color),
                    Program.Program.convert_hex_to_rgb(button_text_color),
                    0.87,
                )
            ),
            "My Pantry",
            self.my_pantry_tab_button.winfo_width(),
            self.my_pantry_tab_button.winfo_height(),
            22,
        )
        self.my_pantry_tab_button.config(
            border="0",
            relief="flat",
            borderwidth=0,
            highlightthickness=0,
            activebackground=bg_color,
            image=self.my_pantry_tab_button_image,
        )

        self.recipes_tab_button_image = Program.Program.create_rounded_button_image(
            bg_color,
            primary_color,
            Program.Program.convert_rgb_to_hex(
                Program.Program.overlay_colors(
                    Program.Program.convert_hex_to_rgb(primary_color),
                    Program.Program.convert_hex_to_rgb(button_text_color),
                    0.38,
                )
            ),
            "Recipes",
            self.my_pantry_tab_button.winfo_width(),
            self.my_pantry_tab_button.winfo_height(),
            22,
        )
        self.recipes_tab_button.config(
            border="0",
            relief="sunken",
            borderwidth=0,
            highlightthickness=0,
            activebackground=bg_color,
            background=bg_color,
            state="normal",
            image=self.recipes_tab_button_image,
        )